/**
  ******************************************************************************
  * @file    m1130_eval_i2c_.c
  * @author  Alpscale Software Team
  * @version V1.0.0
  * @date    19-December-2013
  * @brief   This file provides i2c interface.
  * 
  *          It implements a high level communication layer for read and write 
  *          from/to i2c device. The needed Alpscale hardware resources (I2C and 
  *          GPIO) are defined in m1130_eval.h file, and the initialization is 
  *          performed in I2C_LowLevel_Init() function declared in m1130_eval.c 
  *          file.
  *          You can easily tailor this driver to any other development board, 
  *          by just adapting the defines for hardware resources and 
  *          I2C_LowLevel_Init() function. 
  * 
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, ALPHASCALE SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2013 Alphascale</center></h2>
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "m1130_eval_i2c.h"
#include <stdio.h>
#include <string.h>

/** @addtogroup Utilities
  * @{
  */

/** @addtogroup M1130_EVAL
  * @{
  */

/** @addtogroup M1130_EVAL_I2C
  * @brief      This file includes the I2C driver of M1130-EVAL boards.
  * @{
  */

/** @defgroup M1130_EVAL_I2C_Private_Types
  * @{
  */ 
enum i2c_state {
	STATUS_IDLE = 0,
	STATUS_READ_START,
	STATUS_READ_IN_PROGRESS,
	STATUS_READ_SUCCESS,
	STATUS_WRITE_SUCCESS,
	STATUS_ACKPOLLING,
	STATUS_POLLNOTACKED,
	STATUS_TXDATANOTACKED,
	STATUS_TXABORT_OTHERS,
};

typedef struct{
#define I2C_NAME_SIZE 20
  char name[I2C_NAME_SIZE];
  uint8_t slavAddr; 							/* slave device address */
  uint8_t slaveInternelAddrBits; 	/* Specifies slave device internel address bits of use */
  uint8_t pageSize; 							/* page size for Maximum write data to i2c device */
}I2C_DEVICE_ID;
/**
  * @}
  */

/** @defgroup M1130_EVAL_I2C_Private_Defines
  * @{
  */

// #define DEBUG
#ifdef DEBUG
#define I2C_DEBUG(info, ...) printf("[%s][%d]-"info"", __FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define I2C_DEBUG(info, ...)
#endif

#define I2C_DEVICE_TOTAL_SIZE   0x00020000 //you can set the i2c device total size here accordingly
#define ACKPOLLTIMES 						10000		//we specify the retry number of the write operations to the same page, ie, ACK polling, see I2C device specifications for more
#define MAX_I2C_DMA_BUSY_WATI	  0X200000
#define MAX_I2C_WAIT_TIME	 			0x4000
#define pI2C										I2C0
/**
  * @}
  */

/** @defgroup M1130_EVAL_I2C_Private_Variables
  * @{
  */

int I2C_DEVICE_INTERNEL_ADDR_CYCLES = 2; //you can set the default i2c device internel address cycles here accordingly
int SPEEDMODE = 0;					//0: default speed selection, standard mode 100kHz; 1: fast mode 400kHz

static I2C_DEVICE_ID i2c_dev_ids[] = {
	// name,slavAddr,slaveInternelAddrBits,pageSize
  {"24c02", 0x50, 8, 8},
  // {"24c04", 0x50, 9, 16},
  {"24c08", 0x50, 10, 16},
  // {"24c16", 0x50, 11, 16},
  {"24c256", 0x50, 15, 64},
  // {"wm8731", 0x1a, 8, 8},
};
I2C_DEVICE_ID *i2c_dev_id;

enum i2c_state I2CState;
char *StoreAddr;				  
int RxByteNumRemained;   

/**
  * @}
  */

/** @defgroup M1130_EVAL_I2C_Private_Function_Prototypes
  * @{
  */
/**
  * @}
  */

/** @defgroup M1130_EVAL_I2C_Private_Functions
  * @{
  */


/**
  * @brief  DeInitializes peripherals used by the I2C driver.
  * @param  I2Cx:the target I2C peripheral. 
  * @param  SpeedMode: 0, standard; 1, fast.
  * @retval None
  */
void I2C_LowLevel_DeInit(I2C_TypeDef *I2Cx, int speedmode)
{
#ifdef I2C_IRQ_ENABLE
  NVIC_InitTypeDef NVIC_InitStructure;
#endif
  /* I2C Peripheral Disable */
  I2C_Cmd(I2Cx, DISABLE, speedmode);

  /* I2C DeInit */
  I2C_Reset(I2Cx);

  if (I2Cx == I2C0) {
    /*!< I2C Periph clock disable */
    RCC_SetAHBCLK(1 << AHBCLK_BIT_I2C0, DISABLE);

#ifdef I2C_IRQ_ENABLE
			/*Disable I2C interrupt here*/
			NVIC_InitStructure.NVIC_IRQChannel = I2C0_IRQn;
			NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
			NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
			NVIC_Init(&NVIC_InitStructure);
#endif
  }
}
/**
  * @brief  DeInitializes peripherals used by the I2C driver.
  * @param  None
  * @retval None
  */
void i2cClose(void)
{
	I2C_LowLevel_DeInit(pI2C, SPEEDMODE);
}
/**
  * @brief  Initializes peripherals I2C clock.
  * @param  None
  * @retval None
  */
void I2C_RccInit(I2C_TypeDef* I2Cx)
{
	if(I2Cx == I2C0){
		/* I2C DeInit */
		I2C_Reset(I2Cx);
	}
}

/**
  * @brief Initalizes I2C SDA/SCL GPIO. 
  * @param  I2Cx:the target I2C peripheral. 
  * @param  i2c_io:i2c SDA/SCL gpio init. 
  * 			@arg I2C_GP7:gp7_4,gp7_5
  * 			@arg I2C_GP0_0:gp0_0,gp0_1
  * 			@arg I2C_GP0_3:gp0_3,gp0_4
  * @retval None 
  */
void I2C_PinsInit(I2C_TypeDef *I2Cx, I2C_GPIO i2c_io)
{
	if(I2Cx == I2C0){
		switch (i2c_io)
		{
			case I2C_GP0_0:
				GPIO_SetPinMux(GPIO0, GPIO_Pin_0, GPIO_FUNCTION_2);//gp0_0 sda
				GPIO_SetPinMux(GPIO0, GPIO_Pin_1, GPIO_FUNCTION_2);//gp0_1 scl
				break;
			case I2C_GP0_3:
				GPIO_SetPinMux(GPIO0, GPIO_Pin_3, GPIO_FUNCTION_3);//gp0_3 sda
				GPIO_SetPinMux(GPIO0, GPIO_Pin_4, GPIO_FUNCTION_3);//gp0_4 scl
				break;
			case I2C_GP7:
				GPIO_SetPinMux(GPIO1, GPIO_Pin_28, GPIO_FUNCTION_2);//gp7_4 sda
				GPIO_SetPinMux(GPIO1, GPIO_Pin_29, GPIO_FUNCTION_2);//gp7_5 scl
				break;
			default:
				break;
		}
	}
	
}

/**
  * @brief  Initializes peripherals used by the I2C driver.
  * @param  dev_name:i2c device name. 
	*  This parameter can be a value of @ref i2c_dev_ids.name
  * @param  i2c_io:i2c SDA/SCL gpio init. 
  * 					@arg I2C_GP7:gp7_4,gp7_5
  * 					@arg I2C_GP0_0:gp0_0,gp0_1
  * 					@arg I2C_GP0_3:gp0_3,gp0_4
  * @retval 0: okay; else: fail.
  */
int i2cOpen(char *dev_name, I2C_GPIO i2c_io) 
{
#ifdef I2C_IRQ_ENABLE
  NVIC_InitTypeDef NVIC_InitStructure;
#endif
	I2C_InitTypeDef I2C_InitStruct;
	int i;

	I2C_InitStruct.I2C_Mode = I2C_MASTER_MODE; 
	I2C_InitStruct.I2C_ClockSpeed = I2C_Speedmode_Stand;
	I2C_InitStruct.I2C_10BITADDR_SLAVE = I2C_7BITADDR_SLAVE;
	I2C_InitStruct.I2C_OwnAddress = 0x73;       //casually specify 
	I2C_InitStruct.I2C_10BITADDR_MASTER = I2C_7BITADDR_MASTER;
	I2C_InitStruct.I2C_RESTART_EN = I2C_RESTART_ENABLED;

  for (i = 0; i < (sizeof(i2c_dev_ids) / sizeof(i2c_dev_ids[0])); i++) {
		if(strncmp(dev_name,i2c_dev_ids[i].name,strlen(dev_name)) == 0){
			i2c_dev_id = &i2c_dev_ids[i];
			break;
    }
  }
	if (i == (sizeof(i2c_dev_ids) / sizeof(i2c_dev_ids[0]))){
			I2C_DEBUG("i2c device is not found!\r\n");
	}

	I2C_DEVICE_INTERNEL_ADDR_CYCLES = (i2c_dev_id->slaveInternelAddrBits-1)/8 + 1;
  if ((i2c_dev_id->slaveInternelAddrBits >= (i2c_dev_id->slaveInternelAddrBits / 8 * 8 + 1)) &&
      (i2c_dev_id->slaveInternelAddrBits <= (i2c_dev_id->slaveInternelAddrBits / 8 * 8 + 3)))  //3:p0,p1,p2 memory page addressing for atmel eeprom
  {
    I2C_DEVICE_INTERNEL_ADDR_CYCLES -= 1;
  }
  SPEEDMODE = (I2C_InitStruct.I2C_ClockSpeed == I2C_Speedmode_Stand) ? 0:1;	
	I2CState = STATUS_IDLE;

	I2C_PinsInit(pI2C, i2c_io);
	I2C_RccInit(pI2C);

	/* i2c interrupt request */
#ifdef I2C_IRQ_ENABLE
		if (pI2C == I2C0)
		{
			/*Enable I2C interrupt here*/
			NVIC_InitStructure.NVIC_IRQChannel = I2C0_IRQn;
			NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
			NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
			NVIC_Init(&NVIC_InitStructure);
		}
#endif

	if (I2C_Init(pI2C, &I2C_InitStruct)){
		return -1;
	}else{
		return 0;
	}
}

/**
  * @brief  Writes buffer of data to the I2C device.
  * @param  I2Cx:the target I2C peripheral. 
  * @param  wbuf : pointer to the buffer  containing the data to be written 
  *         to the I2C device.
  * @param  BYTENUMBER : number of bytes to write to the i2c device.
  * @retval 0:okay; else: fail.
  */
int i2c_raw_dma_write(I2C_TypeDef *I2Cx, char *wbuf, int BYTENUMBER)
{
	uint32_t temreg;
	int I2Cblockwait;
	DMAChannel_TypeDef *TxDMAChannel;
  DMAchannel channel;

	if (BYTENUMBER > CHANNEL_MAX_SIZE)
	{
		I2C_DEBUG("i2c tx size beyond dma channel ability\r\n");
		return -1;
	}
  channel = DmaRequestChannel(NULL);
  if (channel >= DMAChannelNum)
  {
    printf("all dma channel busy\r\n");
    return -1;
  }

	TxDMAChannel = (DMAChannel_TypeDef *)(DMA_BASE + 0x58 * channel);

	/*i2c dma tx config*/
	temreg = 0x00000000 + (0 << DMA_CTRL_INT_EN) //INT_EN, ch0 irq disable
			 			+ (0 << DMA_CTRL_DST_TR_WIDTH)		 // DST_TR_WIDTH, des transfer width, should set to HSIZE, here is 000, means 8bit
			 			+ (0 << DMA_CTRL_SRC_TR_WIDTH)		 // SRC_TR_WIDTH, sor transfer width, should set to HSIZE, here is 000, means 8bit
			 			+ (2 << DMA_CTRL_DINC)				 // DINC, des addr increment, des is I2C, so should set to 1x, means no change
			 			+ (0 << DMA_CTRL_SINC)				 // SINC, sor addr increment, src is sram, so should set to 00, means to increase
			 			+ (0 << DMA_DEST_MSIZE)			 // DEST_MSIZE, des burst length, set to 001 means 4 DST_TR_WIDTH per burst transcation
			 			+ (0 << DMA_SRC_MSIZE)				 // SRC_MSIZE, sor burst length, set to 001 means 4 SOR_TR_WIDTH per burst transcation
			 			+ (6 << DMA_TTC_FC)				 // TT_FC,transfer type and flow control,001 means memory to peripheral,peripheral is flow controller
			 			+ (0 << DMA_DMS)					 // DMS, des master select, 0 means ahb master 0
			 			+ (0 << DMA_SMS)					 // SMS, sor master select, 0 means ahb master 0
			 			+ (0 << DMA_LLP_DST_EN)			 // LLP_DST_EN, des block chaining enable, set to 0 disable it
			 			+ (0 << DMA_LLP_SRC_EN);			 // LLP_SOR_EN, sor block chaining enable, set to 0 disable it
	
	/*channel request signal config*/
	if(I2Cx == I2C0)
	{
		DmaChannelCFGset(channel, 0, (REQ_I2C0TX << DMA_CFG_SRC_PER) + (REQ_I2C0TX << DMA_CFG_DEST_PER));
	}

	TxDMAChannel->SAR = (uint32_t)wbuf;
	TxDMAChannel->DAR = (uint32_t)&I2Cx->DATA_CMD;
	TxDMAChannel->LLP = (uint32_t)0;
	TxDMAChannel->CTL_L = temreg;
	TxDMAChannel->CTL_H = BYTENUMBER;

	I2Cx->DMA_TDLR = 1; //dma tx level
	I2Cx->DMA_CR |= 1 << 1; //I2C tx dma enable
	if(DmaStartRoutine(channel, 1))
	{
		I2C_DEBUG("I2C: DMA routine failed for write.");
    DmaFreeChannel(channel);
    return -1;
	}
	I2Cblockwait = MAX_I2C_DMA_BUSY_WATI;
	while ((I2C_CheckFlagStatus(I2Cx, I2C_GetFlagStatus(I2Cx), I2C_IC_STATUS_TFE) == RESET))
	{
		if ((I2Cblockwait--) <= 0)
		{
			I2C_DEBUG("I2C: timeout for current write block\n");
      DmaFreeChannel(channel);
      return -1;
		}
	}
  DmaFreeChannel(channel);
	return 0;
}
	
/**
  * @brief  Reads buffer of data from the i2c device.
  * @param  I2Cx:the target I2C peripheral. 
  * @param  rbuf :i2c device's internal address to start reading from.
  * @param  BYTENUMBER : the variable holding number of bytes to 
  *         be read from the i2c device.
  * @retval 0:okay; else: fail.
  */
int i2c_raw_dma_read(I2C_TypeDef *I2Cx, char *rbuf, int BYTENUMBER)
{
	uint32_t temreg;
	int I2Cblockwait;
	uint32_t TrigRecData = 0x100; //set IC_DATA_CMD bit[8]=1 to trigger read
	DMAChannel_TypeDef *TxDMAChannel,*RxDMAChannel;
  DMAchannel channel_tx, channel_rx;

	if(BYTENUMBER > CHANNEL_MAX_SIZE)
	{
		I2C_DEBUG("i2c tx size beyond dma channel ability\r\n");
		return -1;
	}

  channel_tx = DmaRequestChannel(NULL);
  if (channel_tx >= DMAChannelNum)
    return -1;
  channel_rx = DmaRequestChannel(NULL);
  if (channel_rx >= DMAChannelNum)
  {
    DmaFreeChannel(channel_tx);
    return -1;
  }

  TxDMAChannel = (DMAChannel_TypeDef *)(DMA_BASE + channel_tx*0x58);
  RxDMAChannel = (DMAChannel_TypeDef *)(DMA_BASE + channel_rx*0x58);

	temreg = 0x00000000 + (0 << DMA_CTRL_INT_EN) //INT_EN, ch0 irq disable
			 			+ (2 << DMA_CTRL_DST_TR_WIDTH)		 // DST_TR_WIDTH, des transfer width, should set to HSIZE, here is 000, means 8bit
			 			+ (2 << DMA_CTRL_SRC_TR_WIDTH)		 // SRC_TR_WIDTH, sor transfer width, should set to HSIZE, here is 000, means 8bit
			 			+ (2 << DMA_CTRL_DINC)				 // DINC, des addr increment, des is I2C, so should set to 1x, means no change
			 			+ (2 << DMA_CTRL_SINC)				 // SINC, sor addr increment, src is sram, so should set to 1x, means no change
						+ (0 << DMA_DEST_MSIZE)			 // DEST_MSIZE, des burst length, set to 001 means 4 DST_TR_WIDTH per burst transcation
			 			+ (0 << DMA_SRC_MSIZE)				 // SRC_MSIZE, sor burst length, set to 001 means 4 SOR_TR_WIDTH per burst transcation
			 			+ (1 << DMA_TTC_FC)				 // TT_FC,transfer type and flow control,001 means memory to peripheral,dma is flow controller
			 			+ (0 << DMA_DMS)					 // DMS, des master select, 0 means ahb master 0
			 			+ (0 << DMA_SMS)					 // SMS, sor master select, 0 means ahb master 0
			 			+ (0 << DMA_LLP_DST_EN)			 // LLP_DST_EN, des block chaining enable, set to 0 disable it
			 			+ (0 << DMA_LLP_SRC_EN);			 // LLP_SOR_EN, sor block chaining enable, set to 0 disable it

	TxDMAChannel->SAR = (uint32_t)&TrigRecData;
	TxDMAChannel->DAR = (uint32_t)&pI2C->DATA_CMD;
	TxDMAChannel->LLP = (uint32_t)0;
	TxDMAChannel->CTL_L = temreg;
	TxDMAChannel->CTL_H = BYTENUMBER;
	
	/* i2c dma rx config */
	temreg = 0x00000000 + (0 << DMA_CTRL_INT_EN) //INT_EN, ch1 irq enable
			 			+ (0 << DMA_CTRL_DST_TR_WIDTH)		 // DST_TR_WIDTH, des transfer width, should set to HSIZE, here is 000, means 8bit
			 			+ (0 << DMA_CTRL_SRC_TR_WIDTH)		 // SRC_TR_WIDTH, sor transfer width, should set to HSIZE, here is 000, means 8bit
			 			+ (0 << DMA_CTRL_DINC)				 // DINC, des addr increment, des is SPI, so should set to 1x, means no change
			 			+ (2 << DMA_CTRL_SINC)				 // SINC, sor addr increment, src is sram, so should set to 00, means to increase
			 			+ (0 << DMA_DEST_MSIZE)			 // DEST_MSIZE, des burst length, set to 001 means 4 DST_TR_WIDTH per burst transcation
			 			+ (0 << DMA_SRC_MSIZE)				 // SRC_MSIZE, sor burst length, set to 001 means 4 SOR_TR_WIDTH per burst transcation
			 			+ (2 << DMA_TTC_FC)				 // TT_FC,transfer type and flow control,001 means peripheral to memory,dma is flow controller
			 			+ (0 << DMA_DMS)					 // DMS, des master select, 0 means ahb master 0
			 			+ (0 << DMA_SMS)					 // SMS, sor master select, 0 means ahb master 0
			 			+ (0 << DMA_LLP_DST_EN)			 // LLP_DST_EN, des block chaining enable, set to 0 disable it
			 			+ (0 << DMA_LLP_SRC_EN);			 // LLP_SOR_EN, sor block chaining enable, set to 0 disable it
		/*channel request signal config*/
	if(I2Cx == I2C0)
	{
		DmaChannelCFGset(channel_rx, 0, (REQ_I2C0RX << DMA_CFG_SRC_PER) + (REQ_I2C0RX << DMA_CFG_DEST_PER));
		DmaChannelCFGset(channel_tx, 0, (REQ_I2C0TX << DMA_CFG_SRC_PER) + (REQ_I2C0TX << DMA_CFG_DEST_PER));
	}
	RxDMAChannel->SAR = (uint32_t)&I2Cx->DATA_CMD;
	RxDMAChannel->DAR = (uint32_t)rbuf;
	RxDMAChannel->LLP = (uint32_t)0;
	RxDMAChannel->CTL_L = temreg;
	RxDMAChannel->CTL_H = BYTENUMBER;

	pI2C->DMA_TDLR = 1; //dma tx level
	pI2C->DMA_RDLR = 0; //dma rx level
	pI2C->DMA_CR |= 3; //I2C tx rx dma enable

	DMA->ChEnReg = (0x101 << channel_tx) | (0x101 << channel_rx); //start dma transfer

	I2Cblockwait = MAX_I2C_DMA_BUSY_WATI;
	while(DMA->ChEnReg & (1 << channel_tx)){
		if ((I2Cblockwait--) <= 0)
		{
			I2C_DEBUG("I2C: DMA routine failed for write.\r\n");
			DmaFreeChannel(channel_tx);
			return -1;
		}
	}

	I2Cblockwait = MAX_I2C_DMA_BUSY_WATI;
	while(DMA->ChEnReg & (1 << channel_rx)){
		if ((I2Cblockwait--) <= 0)
		{
			I2C_DEBUG("I2C: DMA routine failed for read.\r\n");
			DmaFreeChannel(channel_rx);
			return -1;
		}
	}

	I2Cblockwait = MAX_I2C_DMA_BUSY_WATI;
	while ((I2C_CheckFlagStatus(I2Cx, I2C_GetFlagStatus(I2Cx), I2C_IC_STATUS_RFNE) == SET))
	{
		if ((I2Cblockwait--) <= 0)
		{
			I2C_DEBUG("I2C: timeout for current read block\n");
      DmaFreeChannel(channel_tx);
      DmaFreeChannel(channel_rx);
			return -1;
		}
	}
  DmaFreeChannel(channel_tx);
  DmaFreeChannel(channel_rx);
  return 0;
}

#define ADDR_CYCLES   4
__align(4) static uint8_t i2c_addr[ADDR_CYCLES+64];	//max pagesize is 64
/**
  * @brief  send i2c slave address.
  * @param  I2Cx:the target I2C peripheral. 
  * @param  i2cSlaveInterelAddr : i2c device's internal address to write to.
  * @retval 0:okay; else: fail.
  */
int i2c_dma_sendAddr(I2C_TypeDef *I2Cx, uint32_t i2cSlaveInterelAddr)
{
	int addrCycles = I2C_DEVICE_INTERNEL_ADDR_CYCLES;
	int i = 0;
	uint8_t p2p1p0 = 0;
	p2p1p0 = (i2cSlaveInterelAddr >> (I2C_DEVICE_INTERNEL_ADDR_CYCLES * 8)) & 0x7;
	i2c_dev_id->slavAddr |= p2p1p0;

	/*now, update device address IC_TAR LSB bits */
	I2C_Cmd(I2Cx, DISABLE, SPEEDMODE);
	I2C_SendSlavAddress(I2Cx, i2c_dev_id->slavAddr);
	I2C_Cmd(I2Cx, ENABLE, SPEEDMODE);
	if(addrCycles > ADDR_CYCLES){
		I2C_DEBUG("i2c slave internel address bits of using is overflow\r\n");
		return -1;
	}
	/* slave internel address */
	do
	{
		i2c_addr[i] = (i2cSlaveInterelAddr >> ((addrCycles - 1) * 8)) & 0xff;
		addrCycles--;
		i++;
	} while (addrCycles > 0);

	return 0;
}

/**
  * @brief  Writes buffer of data to the I2C device.
  * @param  I2Cx:the target I2C peripheral. 
  * @param  wbuf : pointer to the buffer  containing the data to be written 
  *         to the I2C device.
  * @param  i2cSlaveInterelAddr : i2c device's internal address to write to.
  * @param  BYTENUMBER : number of bytes to write to the i2c device.
  * @retval 0:okay; else: fail.
  */
int i2c_dma_write(I2C_TypeDef *I2Cx, char *wbuf, uint32_t i2cSlaveInterelAddr, int BYTENUMBER)
{
	int i;
	int bytenum, writenum;

	if (BYTENUMBER == 0 || (unsigned int)i2cSlaveInterelAddr >= I2C_DEVICE_TOTAL_SIZE)
	{
		I2C_DEBUG("alp_i2c_device_write argument error!\n");
		return -1;
	}
	if (BYTENUMBER > (I2C_DEVICE_TOTAL_SIZE - (unsigned int)i2cSlaveInterelAddr))
	{
		I2C_DEBUG("write all the available space!\n");
		bytenum = I2C_DEVICE_TOTAL_SIZE - (unsigned int)i2cSlaveInterelAddr;
	}
	else{
		bytenum = BYTENUMBER;
	}

	do
	{
		if (((unsigned int)i2cSlaveInterelAddr & (unsigned int)(i2c_dev_id->pageSize - 1)) != 0) //i2cSlaveInterelAddr not page aligned
		{
			writenum = i2c_dev_id->pageSize - ((unsigned int)i2cSlaveInterelAddr & (unsigned int)(i2c_dev_id->pageSize - 1));
			if (writenum > bytenum)
				writenum = bytenum;
		}else{
			writenum = (bytenum > i2c_dev_id->pageSize) ? i2c_dev_id->pageSize : bytenum;
		}

		if(i2c_dma_sendAddr(I2Cx, i2cSlaveInterelAddr)){
			return -1;
		}
		for(i=0;i<writenum;i++){
			i2c_addr[I2C_DEVICE_INTERNEL_ADDR_CYCLES+i] =  wbuf[i];
		}
		if (i2c_raw_dma_write(I2Cx, (char *)i2c_addr, writenum+I2C_DEVICE_INTERNEL_ADDR_CYCLES)) //page write
		{
			return -1;
		}
		// commonDelay(10000);
		wbuf += writenum;
		i2cSlaveInterelAddr += writenum;
		bytenum -= writenum;
	} while (bytenum > 0);
	return 0;	
}

int i2c_dma_read(I2C_TypeDef *I2Cx, char *rbuf, uint32_t i2cSlaveInterelAddr, int BYTENUMBER)
{
	i2c_dma_sendAddr(I2Cx, i2cSlaveInterelAddr);
	if(i2c_raw_dma_write(I2Cx, (char *)i2c_addr, I2C_DEVICE_INTERNEL_ADDR_CYCLES)){
		return -1;
	}
	if (i2c_raw_dma_read(I2Cx, rbuf, BYTENUMBER))
	{
		return -1;
	}
	return 0;
}

/**
  * @brief  Writes buffer of data to the I2C device.
  * @param  wbuf : pointer to the buffer  containing the data to be written 
  *         to the I2C device.
  * @param  i2cSlaveInterelAddr : i2c device's internal address to write to.
  * @param  BYTENUMBER : number of bytes to write to the i2c device.
  * @retval 0:okay; else: fail.
  */
int i2c_raw_no_irq_dma_write(char *wbuf, uint32_t i2cSlaveInterelAddr, int BYTENUMBER)
{
	int i = 0;
	int addrCycles = I2C_DEVICE_INTERNEL_ADDR_CYCLES;
	int wait_time;
	uint8_t p2p1p0 = 0;
	p2p1p0 = (i2cSlaveInterelAddr >> (I2C_DEVICE_INTERNEL_ADDR_CYCLES * 8)) & 0x7;
	i2c_dev_id->slavAddr |= p2p1p0;
	/*now, update device address IC_TAR LSB bits */
	I2C_Cmd(pI2C, DISABLE, SPEEDMODE);
	I2C_SendSlavAddress(pI2C, i2c_dev_id->slavAddr);
	I2C_Cmd(pI2C, ENABLE, SPEEDMODE);

	/* slave internel address */
	do
	{
		I2C_SendData(pI2C, (i2cSlaveInterelAddr >> ((addrCycles-1)*8)) & 0x00ff);
		wait_time = MAX_I2C_WAIT_TIME;
		while ((I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_TFE) == RESET)){
			if (wait_time-- < 0)
		{
				I2C_DEBUG("i2c send slave device internal address timeout\r\n");
				return -1;
			}
		}
		addrCycles--;
	} while (addrCycles > 0);

	/*write data*/
	for (i = 0; i < BYTENUMBER; i++)
	{
		I2C_SendData(pI2C, wbuf[i] & 0x00ff);
		wait_time = MAX_I2C_WAIT_TIME;
		while ((I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_TFE) == RESET)){
			if (wait_time-- < 0)
		{
				I2C_DEBUG("i2c send data timeout\r\n");
				return -1;
			}
		}
	}
	return 0;
}

/**
  * @brief  Writes buffer of data to the I2C device.
  * @param  wbuf : pointer to the buffer  containing the data to be written 
  *         to the I2C device.
  * @param  i2cSlaveInterelAddr : i2c device's internal address to write to.
  * @param  BYTENUMBER : number of bytes to write to the i2c device.
  * @retval 0:okay; else: fail.
  */
int i2c_no_irq_dma_write(char *wbuf, uint32_t i2cSlaveInterelAddr, int BYTENUMBER)
{
	int bytenum, writenum;

	if (BYTENUMBER == 0 || (unsigned int)i2cSlaveInterelAddr >= I2C_DEVICE_TOTAL_SIZE)
	{
		I2C_DEBUG("alp_i2c_device_write argument error!\n");
		return -1;
	}
	if (BYTENUMBER > (I2C_DEVICE_TOTAL_SIZE - (unsigned int)i2cSlaveInterelAddr))
	{
		I2C_DEBUG("write all the available space!\n");
		bytenum = I2C_DEVICE_TOTAL_SIZE - (unsigned int)i2cSlaveInterelAddr;
	}
	else
		bytenum = BYTENUMBER;

	do
	{
		if (((unsigned int)i2cSlaveInterelAddr & (unsigned int)(i2c_dev_id->pageSize - 1)) != 0) //i2cSlaveInterelAddr not page aligned
		{
			writenum = i2c_dev_id->pageSize - ((unsigned int)i2cSlaveInterelAddr & (unsigned int)(i2c_dev_id->pageSize - 1));
			if (writenum > bytenum)
				writenum = bytenum;
		}else
			writenum = (bytenum > i2c_dev_id->pageSize) ? i2c_dev_id->pageSize : bytenum;
		if(i2c_raw_no_irq_dma_write(wbuf, i2cSlaveInterelAddr,writenum)){
			return -1;
		}
		// commonDelay(10000);
		wbuf += writenum;
		i2cSlaveInterelAddr += writenum;
		bytenum -= writenum;
	} while (bytenum > 0);
	return 0;	
}

/**
  * @brief  Reads buffer of data from the i2c device.
  * @param  i2cSlaveInterelAddr : pointer to the buffer that receives the data read from 
  *         the i2c device.
  * @param  rbuf :i2c device's internal address to start reading from.
  * @param  BYTENUMBER : the variable holding number of bytes to 
  *         be read from the i2c device.
  * @retval 0:okay; else: fail.
  */
int i2c_no_irq_dma_read(char *rbuf, uint32_t i2cSlaveInterelAddr, int BYTENUMBER)
{
	int i = 0;
	int num = 0;
	int addrCycles = I2C_DEVICE_INTERNEL_ADDR_CYCLES;
	int wait_time;
	uint8_t p2p1p0 = 0;
	p2p1p0 = (i2cSlaveInterelAddr >> (I2C_DEVICE_INTERNEL_ADDR_CYCLES * 8)) & 0x7;
	i2c_dev_id->slavAddr |= p2p1p0;
	/*slave chip device addressing setting, with the LSB bits for internal addressing set here.*/
	I2C_Cmd(pI2C, DISABLE, SPEEDMODE);
	I2C_SendSlavAddress(pI2C, i2c_dev_id->slavAddr);
	I2C_Cmd(pI2C, ENABLE, SPEEDMODE);
	
	/* slave internel address */
	do
	{
		I2C_SendData(pI2C, (i2cSlaveInterelAddr >> ((addrCycles-1)*8)) & 0x00ff);
		wait_time = MAX_I2C_WAIT_TIME;
		while ((I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_TFE) == RESET)){
			if (wait_time-- < 0)
			{
				I2C_DEBUG("i2c send slave internal address timeout\r\n");
				return -1;
			}
		}
		addrCycles--;
	} while (addrCycles > 0);

	/*tragger receive and read rx fifo*/
	wait_time = MAX_I2C_WAIT_TIME;
	while(i < BYTENUMBER)
	{
		if(I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_TFE) == SET){
			I2C_TriggerRecData(pI2C);
				i++;
		}
		if (I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_RFNE) == SET)
		{
			rbuf[num++] = I2C_ReceiveData(pI2C);
			wait_time = MAX_I2C_WAIT_TIME;
			continue;
		}
		wait_time--;
		if(wait_time < 0){
			I2C_DEBUG("i2c read data timeout\r\n");
			return -1;
		}
	}

	/* wait i2c idle */
	wait_time = MAX_I2C_WAIT_TIME;
	while (I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_ACTIVITY) == SET){
		if (wait_time-- < 0)
		{
			I2C_DEBUG("i2c wait idle timeout\r\n");
			return -1;
		}
	}
	/* read i2c remaining data in rx fifo */
	wait_time = MAX_I2C_WAIT_TIME;
	while ((I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_RFNE) == SET) && (wait_time--))
	{
		rbuf[num++] = I2C_ReceiveData(pI2C);
	}
	if(wait_time < 0){
		I2C_DEBUG("read i2c remaining data in rx fifo timeout\r\n");
		return -1;
	}

	return 0;
}
/**
  * @brief  Reads a block of data from the i2c device.
  * @param  i2cSlaveInterelAddr : pointer to the buffer that receives the data read from 
  *         the i2c device.
  * @param  rbuf :i2c device's internal address to start reading from.
  * @param  BYTENUMBER : the variable holding number of bytes to 
  *         be read from the i2c device.
  *  
  * @retval I2C_OK (0) if operation is correctly performed, else return value 
  *         different from I2C_OK (0) .
  */
int i2c_irq_read(char *rbuf, uint32_t i2cSlaveInterelAddr, int BYTENUMBER)
{
	int err = 0;
	int bytenum = BYTENUMBER;
	int addrCycles = I2C_DEVICE_INTERNEL_ADDR_CYCLES;
	uint8_t p2p1p0 = 0;
	int wait_time;
	StoreAddr = rbuf;
	RxByteNumRemained = BYTENUMBER;
	
		
	p2p1p0 = (i2cSlaveInterelAddr >> (I2C_DEVICE_INTERNEL_ADDR_CYCLES * 8)) & 0x7;
	i2c_dev_id->slavAddr |= p2p1p0;
	if (I2CState != STATUS_IDLE)
	{
		return -1;
	}

	/*slave chip device addressing setting, with the LSB bits for internal addressing set here.*/
	I2C_Cmd(pI2C, DISABLE, SPEEDMODE);
	I2C_SendSlavAddress(pI2C, i2c_dev_id->slavAddr);
	I2C_Cmd(pI2C, ENABLE, SPEEDMODE);

	if (bytenum == 0 || (unsigned int)i2cSlaveInterelAddr >= I2C_DEVICE_TOTAL_SIZE)
	{
		I2C_DEBUG("alp_i2c_device_read argument error!\r\n");
		return -1;
	}

	if ((I2C_DEVICE_TOTAL_SIZE - (unsigned int)i2cSlaveInterelAddr) < bytenum)
	{
		I2C_DEBUG("we only read all available space!\r\n");
		bytenum = I2C_DEVICE_TOTAL_SIZE - (unsigned int)i2cSlaveInterelAddr;
		RxByteNumRemained = I2C_DEVICE_TOTAL_SIZE - (unsigned int)i2cSlaveInterelAddr;
	}

	/*before actural data transferring, enable the releated interrupts*/
	I2C_ClearITPendingBit(pI2C);

	I2C_ITConfig(pI2C, I2C_IT_RX_FULL | I2C_IT_TX_ABRT, ENABLE);

	I2CState = STATUS_READ_START;

	/*For i2c device read, we use Random Read*/
	/*First internal addressing*/
	do
	{
		I2C_SendData(pI2C, (i2cSlaveInterelAddr >> ((addrCycles-1)*8)) & 0x00ff);
		wait_time = MAX_I2C_WAIT_TIME;
		while ((I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_TFE) == RESET)){
			if(wait_time-- < 0){
				I2C_DEBUG("i2c send slave internal address timeout\r\n");
				return -1;
			}
		}
		addrCycles--;
	} while (addrCycles > 0);

	if (I2CState != STATUS_READ_START)
		I2C_DEBUG("I2C read error.\n");

	/*then SEQUENTIAL READ*/
	while ((bytenum--) && ((I2CState == STATUS_READ_START) || (I2CState == STATUS_READ_IN_PROGRESS)))
	{
		if (((I2C_ReadRegister(pI2C, I2C_Register_RXFLR) & ((RX_BUFFER_DEPTH << 1) - 1)) < RX_BUFFER_DEPTH) && ((BYTENUMBER - bytenum - 1) < (StoreAddr - rbuf + RX_BUFFER_DEPTH)))
		{
			I2C_TriggerRecData(pI2C);
		}
		else
		{
			++bytenum;
			continue;
		}
	}

	/*wait until receive finish or go IDLE.*/
	wait_time = MAX_I2C_WAIT_TIME;
	while ((I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_RFNE) != RESET) || (I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_ACTIVITY) == SET)){
		if(wait_time-- < 0){
			I2C_DEBUG("i2c wait idle timeout\r\n");
			return -1;
		}
	}

	/*Disable all interrupts*/
	I2C_ITConfig(pI2C, I2C_IT_ALL, DISABLE);

	/* Clear all interrupts */
	I2C_ClearITPendingBit(pI2C);


	if (I2CState != STATUS_READ_SUCCESS)
	{
		err = -I2CState;
		I2CState = STATUS_IDLE;
		I2C_DEBUG("i2c read data fail\r\n");
		return err;
	}
	else
	{
		I2CState = STATUS_IDLE;
		return 0;
	}
}

/**
  * @brief  low level iic device write functions.
  *
  * @note   The number of bytes (combined to write start address) must not 
  *         cross the i2c device page boundary. This function can only write into
  *         the boundaries of an i2c device page.
  *         This function doesn't check on boundaries condition (in this driver 
  *         the function I2C_WriteBuffer() which calls I2C_raw_polling_write() is 
  *         responsible of checking on Page boundaries).
  * 
  * @param  wbuf : pointer to the buffer containing the data to be written to 
  *         the i2c device.
  * @param  i2cSlaveInterelAddr :i2c device's internal address to write to.
  * @param  BYTENUMBER : umber of bytes needed to be handled.
  *
  * @retval 1:write operation succeeded 2: transmit data not acked 3: address ACK polling failed eventually
  */
int I2C_raw_polling_write(char *wbuf, uint32_t i2cSlaveInterelAddr, int BYTENUMBER)
{
	int bytenum = BYTENUMBER;
	int retry = ACKPOLLTIMES;
	int addrCycles = I2C_DEVICE_INTERNEL_ADDR_CYCLES;
	uint8_t p2p1p0 = 0;
	int wait_time;

	p2p1p0 = (i2cSlaveInterelAddr >> (I2C_DEVICE_INTERNEL_ADDR_CYCLES * 8)) & 0x7;
	i2c_dev_id->slavAddr |= p2p1p0;
	/*now set device address IC_TAR LSB bits*/
	I2C_Cmd(pI2C, DISABLE, SPEEDMODE);
	I2C_SendSlavAddress(pI2C, i2c_dev_id->slavAddr);
	I2C_Cmd(pI2C, ENABLE, SPEEDMODE);

	while ((retry--) && (I2CState == STATUS_ACKPOLLING))
	{
		bytenum = BYTENUMBER;

		/*before actural data transferring, enable the releated interrupts*/
		I2C_ClearITPendingBit(pI2C);
		/*First, only enable TX_ABORT interrupt*/
		I2C_ITConfig(pI2C, I2C_IT_TX_ABRT, ENABLE);
		/*internal addressing*/
		do
		{
			I2C_SendData(pI2C, (i2cSlaveInterelAddr >> ((addrCycles - 1) * 8)) & 0x00ff);
			wait_time = MAX_I2C_WAIT_TIME;
			while ((I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_TFE) == RESET)){
				if (wait_time-- < 0){
					I2C_DEBUG("i2c send slave internal address timeout\r\n");
					return -1;
				}
			}
			addrCycles--;
		} while (addrCycles > 0);

		/*Not just ACK Polling, it also does data transmission*/
		/*enable TX_ABORT&TX_EMPTY interrupts*/
		I2C_ITConfig(pI2C, I2C_IT_TX_ABRT | I2C_IT_TX_EMPTY, ENABLE);
		/*Then data transmission*/
		while ((bytenum--) && (I2CState != STATUS_POLLNOTACKED) && (I2CState != STATUS_TXDATANOTACKED) && (I2CState != STATUS_TXABORT_OTHERS))
		{
			if ((I2C_ReadRegister(pI2C, I2C_Register_TXFLR) & ((TX_BUFFER_DEPTH << 1) - 1)) == TX_BUFFER_DEPTH) //never send more data beyond the TX buffer limit
			{
				++bytenum;
				continue;
			}
			I2C_SendData(pI2C, (unsigned int)(*wbuf++));
		}
		// /*wait until IDLE*/
		wait_time = MAX_I2C_WAIT_TIME;
		while ((I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_TFE) == RESET) || (I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_ACTIVITY) == SET)){
			if (wait_time-- < 0)
			{
				I2C_DEBUG("i2c wait idle timeout\r\n");
				return -1;
			}
		}

		if (I2CState == STATUS_WRITE_SUCCESS)
		{
			return 0;
		}
		else if (I2CState == STATUS_TXDATANOTACKED)
		{
			return -STATUS_TXDATANOTACKED;
		}
		else if (I2CState == STATUS_TXABORT_OTHERS)
		{
			return -STATUS_TXABORT_OTHERS;
		}
		else
			I2CState = STATUS_ACKPOLLING;
	}

	/* ACK polling before write failed finally*/
	return -1;
}

/**
  * @brief  Wait for i2c device Standby state.
  * 
  * @note  This function allows to wait and check that i2c device has finished the 
  *        last operation. It is mostly used after Write operation: after receiving
  *        the buffer to be written, the i2c device may need additional time to actually
  *        perform the write operation. During this time, it doesn't answer to
  *        I2C packets addressed to it. Once the write operation is complete
  *        the i2c device responds to its address.
  * 
  * @param  i2cSlaveInterelAddr:i2c device's internal address to write to.
  * @retval I2C_OK (0) if operation is correctly performed, else return value 
  *         different from I2C_OK (0) .
  */
int I2C_polling(uint32_t i2cSlaveInterelAddr)
{
	volatile uint32_t RegValue = 0;
	int retry = ACKPOLLTIMES;
	int addrCycles = I2C_DEVICE_INTERNEL_ADDR_CYCLES;
	int wait_time;
	while ((retry--) && (I2CState == STATUS_ACKPOLLING))
	{
		/*before actural data transferring, enable the releated interrupts*/
		I2C_ClearITPendingBit(pI2C);
		/*First, only enable TX_ABORT interrupt*/
		I2C_ITConfig(pI2C, I2C_IT_TX_ABRT, ENABLE);
		/*internal addressing*/
		do
		{
			I2C_SendData(pI2C, (i2cSlaveInterelAddr >> ((addrCycles - 1) * 8)) & 0x00ff);
			wait_time = MAX_I2C_WAIT_TIME;
			while ((I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_TFE) == RESET)){
				if(wait_time-- < 0){
					I2C_DEBUG("i2c send slave internal address\r\n");
					return -1;
				}
			}
			addrCycles--;
		} while (addrCycles > 0);

		/*Just ACK Polling, no data transmission*/
		wait_time = MAX_I2C_WAIT_TIME;
		while ((I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_TFE) == RESET) || (I2C_CheckFlagStatus(pI2C, I2C_GetFlagStatus(pI2C), I2C_IC_STATUS_ACTIVITY) == SET)){
			if(wait_time-- < 0){
				I2C_DEBUG("i2c wait idle timeout\r\n");
				return -1;
			}
		}

		if (I2CState != STATUS_ACKPOLLING)
		{
			if (I2CState != STATUS_POLLNOTACKED)
			{
				return -I2CState;
			}

			I2CState = STATUS_ACKPOLLING;
			RegValue = pI2C->CLR_TX_ABRT;

			continue;
		}
		else
			return 0;
	}

	return -1;
}

/**
  * @brief  Writes buffer of data to the I2C device.
  * @param  wbuf : pointer to the buffer  containing the data to be written 
  *         to the I2C device.
  * @param  i2cSlaveInterelAddr : i2c device's internal address to write to.
  * @param  BYTENUMBER : number of bytes to write to the i2c device.
  * @retval 0:okay; else: fail.
  */
int i2c_irq_write(char *wbuf, uint32_t i2cSlaveInterelAddr, int BYTENUMBER)
{
	int bytenum;
	int ret;
	int err = 0;
	int writenum;

	if (I2CState != STATUS_IDLE)
	{
		return -1;
	}

	if (BYTENUMBER == 0 || (unsigned int)i2cSlaveInterelAddr >= I2C_DEVICE_TOTAL_SIZE)
	{
		I2C_DEBUG("alp_i2c_device_write argument error!\n");
		return -1;
	}

	if (BYTENUMBER > (I2C_DEVICE_TOTAL_SIZE - (unsigned int)i2cSlaveInterelAddr))
	{
		I2C_DEBUG("write all the available space!\n");
		bytenum = I2C_DEVICE_TOTAL_SIZE - (unsigned int)i2cSlaveInterelAddr;
	}
	else
		bytenum = BYTENUMBER;

	I2CState = STATUS_ACKPOLLING;

	do
	{
		if (((unsigned int)i2cSlaveInterelAddr & (unsigned int)(i2c_dev_id->pageSize - 1)) != 0) //i2cSlaveInterelAddr not page aligned
		{
			writenum = i2c_dev_id->pageSize - ((unsigned int)i2cSlaveInterelAddr & (unsigned int)(i2c_dev_id->pageSize - 1));
			if (writenum > bytenum)
				writenum = bytenum;
		}else
			writenum = (bytenum > i2c_dev_id->pageSize) ? i2c_dev_id->pageSize : bytenum;

		ret = I2C_raw_polling_write(wbuf, i2cSlaveInterelAddr, writenum);
		if (ret != 0){
			err = ret;
			goto SendErr;
		}else{
			I2CState = STATUS_ACKPOLLING;
			if ((ret = I2C_polling(i2cSlaveInterelAddr)) != 0) //ACKNOWLEDGE POLLING
			{
				err = ret;
				goto PollingErr;
			}
			wbuf += writenum;
			i2cSlaveInterelAddr += writenum;
			bytenum -= writenum;
			I2CState = STATUS_ACKPOLLING;
		}

	} while (bytenum > 0);

	goto SendOK;

SendErr:

PollingErr:
	I2C_DEBUG("IIc write failed! Bytes remained num: %d!\r\n", (unsigned int)bytenum);

SendOK:
	I2CState = STATUS_IDLE;
	/*Disable all interrupts*/
	I2C_ITConfig(pI2C, I2C_IT_ALL, DISABLE);
	/* Clear all interrupts */
	I2C_ClearITPendingBit(pI2C);
	return err;
}

/**
  * @brief  Reads buffer of data from the i2c device.
  * @param  rbuf :pointer to the buffer that receives the data read from 
  *         the i2c device.
  * @param  i2cSlaveInterelAddr :i2c device's internal address to start reading from. 
  * @param  BYTENUMBER : the variable holding number of bytes to 
  *         be read from the i2c device.
  * @retval 0:okay; else: fail.
  */
int i2cRead(char *rbuf, uint32_t i2cSlaveInterelAddr, int BYTENUMBER)
{
	int ret;
#ifdef I2C_IRQ_ENABLE
  ret = i2c_irq_read(rbuf, i2cSlaveInterelAddr, BYTENUMBER);
#elif defined I2C_DMA_ENABLE
		ret = i2c_dma_read(pI2C, rbuf, i2cSlaveInterelAddr, BYTENUMBER);
#else
		ret = i2c_no_irq_dma_read(rbuf, i2cSlaveInterelAddr, BYTENUMBER);
#endif
  return ret;
}

/**
  * @brief  Writes buffer of data to the I2C device.
  * @param  wbuf : pointer to the buffer  containing the data to be written 
  *         to the I2C device.
  * @param  i2cSlaveInterelAddr : i2c device's internal address to write to.
  * @param  BYTENUMBER : number of bytes to write to the i2c device.
  * @retval 0:okay; else: fail.
  */
int i2cWrite(char *wbuf, uint32_t i2cSlaveInterelAddr, int BYTENUMBER)
{
	int ret;
#ifdef I2C_IRQ_ENABLE
		ret = i2c_irq_write(wbuf, i2cSlaveInterelAddr, BYTENUMBER);
#elif defined I2C_DMA_ENABLE
		ret = i2c_dma_write(pI2C, wbuf, i2cSlaveInterelAddr, BYTENUMBER);
#else
		ret = i2c_no_irq_dma_write(wbuf, i2cSlaveInterelAddr, BYTENUMBER);
#endif
	return ret;
}
/**
  * @brief  Handle transfer abortions and print error messages.
  * @note   By reading register IC_TX_ABRT_SOURCE, various transfer errors can be
  *		    distingushed. At present, no circumstances have been found out that
  *         multiple errors would be occured simutaneously, so we simply use the
  *			register value directly.
  *	   
  * @param  abort: IC_TX_ABRT_SOURCE register value.
  *
  * @retval none.
  */
static void i2c_abort_check(unsigned int abort)
{

	/* Single transfer error check:
	* According to databook, TX/RX FIFOs would be flushed when
	* the abort interrupt occured.
	*/
	if (abort & (1 << 11))
	{
		I2C_DEBUG("ABRT_MASTER_DIS: initiate master operation with master mode disabled.\r\n");
	}
	if (abort & (1 << 10))
	{
		I2C_DEBUG("ABRT_10B_RD_NORSTRT: RESTART disabled and master sent READ cmd in 10-bit addressing.\r\n");
	}

	if (abort & (1 << 9))
	{
		I2C_DEBUG("ABRT_SBYTE_NORSTRT: RESTART disabled and user is trying to send START byte.\r\n");
	}

	if (abort & (1 << 7))
	{
		I2C_DEBUG("ABRT_SBYTE_ACKDET: START byte was not acknowledged.\r\n");
	}
	if (abort & (1 << 3))
	{
		I2C_DEBUG("ABRT_TXDATA_NOACK: No acknowledgement received from slave.\r\n");
	}
	if (abort & (1 << 2))
	{
		I2C_DEBUG("ABRT_10ADDR2_NOACK: The 2nd address byte of the 10-bit address was not acknowledged.\r\n");
	}
	if (abort & (1 << 1))
	{
		I2C_DEBUG("ABRT_10ADDR1_NOACK: The 1st address byte of 10-bit address was not acknowledged.\r\n");
	}
	if (abort & (1 << 0))
	{
		I2C_DEBUG("ABRT_7B_ADDR_NOACK: I2C slave device not acknowledged.\r\n");
	}

	/*clear TX_ABRT bit*/
	I2C_Clear_TX_ABRT_SOURCE(pI2C, abort);
}

/*Actual IIC read implementation, called by ISR*/
void i2c_isr_read(void)
{
	int rx_num;

	rx_num = I2C_ReadRegister(pI2C, I2C_Register_RXFLR) & ((RX_BUFFER_DEPTH << 1) - 1);

	for (; (RxByteNumRemained > 0) && (rx_num > 0); RxByteNumRemained--, rx_num--)
	{
		*StoreAddr++ = (char)(0xff & I2C_ReadRegister(pI2C, I2C_Register_DATA_CMD));
	}

	if (RxByteNumRemained > 0)
	{
		I2CState = STATUS_READ_IN_PROGRESS;
	}
	else
		I2CState = STATUS_READ_SUCCESS;

	return;
}

void I2C0_IRQHandler(void)
{
	unsigned int stat = I2C_GetITFlag(pI2C);
	unsigned int rawabortsource = 0;
	if (!stat)
		return;

	stat &= 0x54;
	if ((I2CState != STATUS_READ_START) && (I2CState != STATUS_READ_IN_PROGRESS) && (I2CState != STATUS_ACKPOLLING))
	{
		goto err;
	}

	if (stat & (1 << 6)) //TX_ABRT
	{

		rawabortsource = I2C_ReadRegister(pI2C, I2C_Register_TX_ABRT_SOURCE);
		I2C_DEBUG("rawabortsource: %4x\r\n", rawabortsource);
	}

	I2C_ClearITPendingBit(pI2C);

	if (stat & (1 << 6)) //TX_ABRT
	{
		i2c_abort_check(rawabortsource);
		if ((rawabortsource & (1 << 0)) && (!(rawabortsource & (1 << 3))))
		{
			I2CState = STATUS_POLLNOTACKED;
		}
		else if ((rawabortsource & (1 << 3)) && (!(rawabortsource & (1 << 0))))
		{
			I2CState = STATUS_TXDATANOTACKED;
		}
		else
		{
			I2CState = STATUS_TXABORT_OTHERS;
			I2C_DEBUG("Unexpected TX_ABORT..\r\n");
			goto err;
		}
		goto exit;
	}

	if (stat & (1 << 2)) //RX_FULL
	{
		i2c_isr_read();
		goto exit;
	}

	if (stat & (1 << 4)) //TX_EMPTY
	{
		if (I2C_GetFlagStatus(pI2C) & I2C_IC_STATUS_TFE)
		{
			I2CState = STATUS_WRITE_SUCCESS;
		}
	}

exit:
	if (I2CState == STATUS_READ_SUCCESS ||
			I2CState == STATUS_WRITE_SUCCESS ||
			I2CState == STATUS_POLLNOTACKED ||
			I2CState == STATUS_TXDATANOTACKED)
	{
		/* Expected success or expected error, clear all interrupts and mask the TX_EMPTY and others */
		I2C_ClearITPendingBit(pI2C);
		I2C_ITConfig(pI2C, I2C_IT_ALL, DISABLE);

		return;
	}

	if (I2CState == STATUS_READ_IN_PROGRESS)
		return;

err:
	/*unexpected error or circumstance happened*/
	I2C_ClearITPendingBit(pI2C);
	I2C_ITConfig(pI2C, I2C_IT_ALL, DISABLE);

	return;
}

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/******************* (C) COPYRIGHT 2013 Alpscale *****END OF FILE****/
